package xyz.scootaloo.mono.data.service

import cn.hutool.log.LogFactory
import org.ktorm.global.count
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import xyz.scootaloo.mono.base.lang.Call
import xyz.scootaloo.mono.base.service.async.AsyncTaskService
import xyz.scootaloo.mono.base.util.IpUtil
import xyz.scootaloo.mono.data.entity.auth.*
import java.time.LocalDateTime
import javax.servlet.http.HttpServletRequest

/**
 * 权限相关操作专用service
 *
 * 专门用来集中管理数据库中以`auth`开头的数据表
 *
 * 管理用户, 角色相关的基本增删改查
 *
 * @author flutterdash@qq.com
 * @since 2021/7/25 20:20
 */
@Service
class AuthService {
    private val log = LogFactory.get()

    @Autowired private lateinit var async: AsyncTaskService

    fun getUserTableRecordCount() = AuthUsers.count()

    fun listUsers(pageNum: Int, pageSize: Int): List<AuthUser> {
        val limit = if (pageSize < 1) 1 else pageSize
        val offset = if (pageNum < 0) 0 else pageNum * limit
        return AuthUsers.list(offset, limit)
    }

    fun listAuthRoles(): List<AuthRole> = AuthRoles.list()

    fun listAuthMenus(): List<AuthMenu> = AuthMenus.list()

    fun findAuthUserByUid(uid: Int): AuthUser? = AuthUsers.findByUid(uid)

    fun findAuthUserByName(name: String): AuthUser? = AuthUsers.findByUsername(name)

    /* 此操作会更新密码和盐 */
    fun updateUserInfo(user: AuthUser): Int = AuthUsers.updateInfo(user)

    fun updateUserLastLogin(uid: Int) = async.delay(60 * 1) {
        AuthUsers.updateLastLogin(uid)
    }

    fun updateUserStatus(request: HttpServletRequest, uid: Int, newStatus: Int): Int {
        return operateWithLog {
            AuthUsers.updateStatus(uid, newStatus)
        }.logWithRequest(request) {
            name = "用户"
            type = "更新"
            this.uid = uid
            desc = "status:$newStatus"
        }
    }

    fun updateUserRoleCode(request: HttpServletRequest, uid: Int, newRoleCode: Int): Int {
        return operateWithLog {
            AuthUsers.updateRoleCode(uid, newRoleCode)
        }.logWithRequest(request) {
            name = "用户"
            type = "更新"
            this.uid = uid
            desc = "roleCode:$newRoleCode"
        }
    }

    fun addAuthUser(request: HttpServletRequest, authUser: AuthUser): Int {
        return operateWithLog {
            AuthUsers.insert(authUser)
        }.logWithRequest(request) {
            name = "用户"
            type = "增加"
            uid = authUser.uid
            desc = "username:${authUser.username}"
        }
    }

    fun addAuthRole(request: HttpServletRequest, authRole: AuthRole): Int {
        return operateWithLog {
            AuthRoles.insert(authRole)
        }.logWithRequest(request) {
            name = "角色"
            type = "增加"
            desc = "${authRole.name}:${authRole.code}"
        }
    }

    fun deleteAuthUserByUid(request: HttpServletRequest, uid: Int): Int {
        return operateWithLog {
            AuthUsers.deleteByUid(uid)
        }.logWithRequest(request) {
            name = "用户"
            type = "删除"
            desc = "uid:${uid}"
        }
    }

    // log operation

    private fun operateWithLog(operate: () -> Int): Call<Int> {
        return try {
            val rsl = operate()
            if (rsl == 0) // 影响到的数据表行数为0, 即无效操作
                return Call.fail(0)
            Call.success(rsl)
        } catch (throwable: Throwable) {
            log.debug(throwable)
            Call.fail(0)
        }
    }

    private fun Call<Int>.logWithRequest(
        request: HttpServletRequest, init: AuthLog.(Call<Int>) -> Unit
    ): Int {
        val log = createLogObject(request, this, init)
        return this.get().apply {
            async.logging {
                AuthLogs.insert(log)
            }
        }
    }

    private fun createLogObject(
        request: HttpServletRequest,
        call: Call<Int>,
        init: AuthLog.(Call<Int>) -> Unit
    ): AuthLog = AuthLog {
        success = call.success
        date = LocalDateTime.now()
        ip = IpUtil.getRealIpAddress(request)
    }.apply { init(call) }
}
